<?php
ArrowController::importModel("org.arrowplatform.controls.forms.ArrowFormField");
ArrowController::importModel("org.arrowplatform.controls.forms.IFormMultipleField");
ArrowController::importModel("org.arrowplatform.controls.forms.IFormValuesSetter");
ArrowController::importModel("org.arrowplatform.controls.forms.IFormContentProvider");
ArrowController::importModel("org.arrowplatform.controls.forms.StandardFormValuesSetter");
ArrowController::importModel("org.arrowplatform.controls.forms.IArrowFormField");

/**
 * Class Persistent 
 *
 * Form is main class  
 * to generate HTML code. 
 *
 * @author Artur Kmera 
 * @version 0.9
 * @package Arrow
 * @subpackage Forms
 */

class ArrowForm extends ArrowTagContainer{

	
	/**
	 * Name of var used to passing form data by POST
	 *
	 */
	const FORM_CONTROL_VAR = "formControl";
	
	/**
	 * ArrowForm errors ( a names of fields with errors )
	 *
	 * @var unknown_type
	 */
	private $errors = array( );
	
	/**
	 * ArrowForm validator
	 * Used to validate form fields after submit
	 *
	 * @var IFormValidator
	 */
	private $validator = "org.arrowplatform.controls.forms.ArrowFormDefaultValidator";
	

	/**
	 * Values that will be used by fields after submit 
	 * and error return (values changed by user in process of
	 * form validation)
	 * Structure:
	 * "field_name" => "proposed_value"
	 *
	 * @var array
	 */
	public  $proposedValues = array();
	

	
	/**
	 * Alert from outside of form
	 * Specyfied dynamicaly by user or by overwriting fields
	 * alerts system
	 *
	 * @var array
	 */
	private $externalAlerts = array();
	
	/**
	 * Fields of form
	 *
	 * @var Field[]
	 */
	private $fields = array();
	
	/**
	 * Content provider
	 *
	 * @var IFormContentProvider
	 */
	private $contentProvider = null;
	
	/**
	 * Values setter
	 *
	 * @var IFormValuesSetter
	 */
	private $valuesSetter = null;
	
	
	public function configure(){
		
		//$this->setErrorOnUnknowAttribute( false );
	
		$this->addProperties(array(
				"actionBean" => false,
				"remoteBean" => false,
				"template" => false,
				"address" => false,
				"namespace" => false,
				"method" => "post",
				"valuesSetter" => false,
				"contentProvider" => false,
				"accessManager" => false,
				"validator" => false,
				"enctype" => false,
				"id" => false,
				"style" => "",
				"class" => "",
				"validateDelegate" => ""
		));
		
		$this->addRequiredProperties(array("ctrlId"));
	}
	
	/**
	 * Loading properties and external properties
	 */
	public function init(){
		

		//Initial values
		$input = ArrowView::getPageInput();
		if(isset( $input[ArrowForm::FORM_CONTROL_VAR][$this->getGlobalId()] )){
			$tmp = $input[ArrowForm::FORM_CONTROL_VAR][$this->getGlobalId()];
			$this->setProposedValues($tmp["proposedValues"]);
			$this->externalAlerts = $tmp["externalAlerts"];
			$this->setErrors($tmp["errors"]);
		}
		//setting namespace
		$namespace = $this->getProperty("namespace");
		if($namespace !== false){
			if(!empty($namespace))
				$this->setProperty("namespace",  $namespace);
			else		
				$this->setProperty("namespace",  false);
		}else{
			$this->setProperty("namespace",  $this->getProperty("ctrlId"));
		} 
		
		$contentProvider = $this->getProperty("contentProvider");
		if( $contentProvider != false )
			$this->setContentProvider($contentProvider);
			
		
			
		$valuesSetter = $this->getProperty("valuesSetter");
		if( $valuesSetter != false )
			$this->setValuesSetter($valuesSetter);
		else{
			$dataSource = $this->getFirstChild("ArrowCtrlDatasource");
			if($dataSource != null)
				$this->setValuesSetter( new StandardFormValuesSetter($dataSource) );
		}
			
		$validator = $this->getProperty("validator");
		if( $validator != false ) 
			$this->setValidator($validator);
			
			
		if( $this->getProperty("id") )
			$id = $this->getProperty("id");
		else
			$id = self::CTRL_NAMESPACE.'-form'.$this->getGlobalId();
			
		$jsData = array();
		$this->registerJsFile(  ARROW_MODELS_PATH."/org/arrowplatform/controls/forms/ctrl/resources/jquery.maskedinput-1.2.2.min.js");
		$this->registerJs( get_class($this), dirname(__FILE__)."/resources/ArrowForm.js", "#".$id, $jsData );
			
	}
	
	/**
	 * Used for generating HTML attributes from external properties
	 *
	 * @return string
	 */
	protected function addExternal(){
		$str="";
		foreach($this->getExternalProperties() as $paramName=>$paramValue){
			$str.= $paramName.'="'.$paramValue.'" ';
		}
		return $str;
	}	
	

	
	public function getFields(){
		$fields = array();
		foreach( $this->getAllChildren() as $field ){
			if($field instanceof IArrowFormField)
				$fields[] = $field;
		}
		return $fields;
	}
	
	/**
	 * Returns form namespace
	 *
	 * @return string
	 */
	public function getNamespace(){
		return $this->getProperty("namespace");
	}
	
	/**
	 * Returns proposed values
	 *
	 * @return array
	 */
	public function getProposedValues(){
		return $this->proposedValues;
	}
	
	/**
	 * Setting proposed values
	 *
	 * @param array $proposedValues
	 */
	public function setProposedValues( $proposedValues ){
		$this->proposedValues = $proposedValues;
	}
	
	/**
	 * Sets content provider
	 *
	 * @param IFormContentProvider $provider //IFormContentProvider
	 */
	public function setContentProvider( $provider ){
		if( is_object($provider) ){
			$this->contentProvider = $provider;
		}else{
			ArrowController::importModel( $provider );
			$className = array_pop(explode( ".", $provider ));
			$this->contentProvider = $className;
		}
	}

	
	/**
	 * Returns content of form if is set content provider 
	 * otherwise gives empty array. If $field is specyfied returns only one
	 * field value
	 *
	 * @param string $field
	 * @return mixed
	 */
	public function getContent( $field = false ){
		if( $this->contentProvider )
			return call_user_func(array($this->contentProvider, "getFormContent"), $this->getId(), $field, $this->getValuesSetter() );
		else
			return null;
	}	
	
	/**
	 * Setting form values setter
	 *
	 * @param IFormContentSetter $setter
	 */
	public function setValuesSetter( IFormValuesSetter $setter ){
		$this->valuesSetter = $setter;
	}
	
	/**
	 * Returns value setter
	 *
	 * @return IFormValuesSetter
	 */
	public function getValuesSetter( ){
		return $this->valuesSetter;
	}
	
	/**
	 * Generates form head code
	 *
	 * @return string
	 */
	public function generateBlockStart(){
		
		
		
		$attributes = array();
		if( $id = $this->getProperty("id") )
			$attributes["id"] = $id;
		else
			$attributes["id"] = self::CTRL_NAMESPACE.'-form'.$this->getGlobalId();
			
		$attributes["method"] = $this->getProperty("method");
		if( $class = $this->getProperty("class") )
			$attributes["class"] = $class;
	
		
		if( $this->getProperty("actionBean") ) {
			$args = array( "path" => $this->getProperty("actionBean") );
			if($this->getProperty("template")) $args["return_tpl"] = $this->getProperty("template"); 
			$action = ArrowTemplateLinker::getDefault()->generateBeanLink( $args );
		} elseif( $this->getProperty("remoteBean") ) {
			$args = array( "path" => $this->getProperty("remoteBean") );
			if($this->getProperty("template")) $args["return_tpl"] = $this->getProperty("template"); 
			$action = ArrowTemplateLinker::getDefault()->generateBeanRemoteAccesLink( $args, true );
		} else {
			$args = array();
			if($this->getProperty("template")) $args["return_tpl"] = $this->getProperty("template");
			$action = ArrowTemplateLinker::getDefault()->generateTemplateLink( $args );
		}
		
		$attributes["action"] = $action."&amp;mediator=ArrowFormMediator";
		if ($this->getProperty("style"))
			$attributes["style"] = $this->getProperty("style");
			
		if ($this->getProperty("enctype"))
			$attributes["enctype"] = $this->getProperty("enctype");
				
		$str = '<form '.ArrowFormField::genTagsFromArray( $attributes ).' '.$this->addExternal().'>'."\n";
		$str.='<input type="hidden" name="'.self::FORM_CONTROL_VAR.'" value="'.$this->getGlobalId().'" />'."\n";
		
		return $str;
	}
	
	/**
	 * Generates form footer
	 *
	 * @return string
	 */
	public function generateBlockEnd(  ){
		$str= "</form>\n";
		$this->saveForm();
		return $str;
	}
	
	/**
	 * Returns field of given name
	 *
	 * @param string $name
	 * @return ArrowFormField
	 */
	public function getFieldByName( $name ){
		foreach( $this->getFields() as $field ){
			if( $field->getName() == $name ){
				return $field;
			}
		}
		
		return false;
	}
	
	/**
	 * Returns form validator path
	 *
	 * @return string
	 */
	public function getValidator(){
		if( $this->getProperty("validator") != false)
			return $this->getProperty("validator");
		return $this->validator;
	}
	
	/**
	 * Sets validator path
	 *
	 * @param string $validatorPath
	 */
	public function setValidator( $validatorPath ){
		$this->validator = $validatorPath;
	}
	
	/**
	 * Returns form errors or false if no errors
	 *
	 * @return mixed
	 */
	public function getErrors(){
		if(empty($this->errors))
			return false;
		return $this->errors;
	}
	
	/**
	 * Sets form errors
	 *
	 * @param array $errors
	 */
	public function setErrors( $errors ){
		$this->errors = $errors;
	}
	
	/**
	 * Returns external alerts or false if no alerts
	 *
	 * @return mixed
	 */
	public function getExternalAlerts(){
		if(empty($this->externalAlerts))
			return false;
		return $this->externalAlerts;
	}
	
	/**
	 * Sets external alerts
	 *
	 * @param array $alerts
	 */
	public function setExternalAlerts( $alerts ){
		$this->externalAlerts = $alerts;
	}	

	/**
	 * Returns form global Id
	 *
	 * @param string $localId
	 * @return string
	 */
	public function getGlobalId(){
		$str = ArrowRouter::getDefault()->getTemplate()->getId();
		return md5( $this->getProperty("ctrlId").$str );
	}	
	/**
	 * Returns page form by Id
	 *
	 * @param string $id
	 * @return Form
	 */
	public static function loadForm( $globalId ){
		//if( $globalId === false ) return array() ; 
		
		foreach( ArrowFormCtrlManager::getDefault()->getRegistredFields() as $fieldModel )
			ArrowController::importModel( $fieldModel );
		
		return unserialize( file_get_contents(ARROW_CACHE_PATH."/form_".$globalId.".php") );
	}
	
	/**
	 * Saves form
	 *
	 * @param Form $form
	 */
	public function saveForm(){
		unset($this->contentProvider);
		unset($this->valuesSetter);
		$content = serialize($this);
		file_put_contents( ARROW_CACHE_PATH."/form_".$this->getGlobalId().".php", $content );	
	}
	
	
	public function checkAccess( $name ) {
		$am = $this->getProperty( "accessManager" );
		if( $am === false ) return true;
		return $am->checkAccesToField( $name, $this->getId() ) ;
	}
		
	
	
	/**
	 * Setting initial data for form ( form with given $formId )
	 * When form will be loadig it'll read from this data 
	 *
	 * @param string $formId 
	 * @param array $errors array of fields that have to display error
	 * @param array $proposedValues values proposed for form fields
	 * @param array $alerts alerts defined by user
	 */
	public static function setDataTransfer( $globalId, $errors, $proposedValues, $alerts = array() ){
		$data = array();
		$data[ArrowForm::FORM_CONTROL_VAR][$globalId] = array();
		$data[ArrowForm::FORM_CONTROL_VAR][$globalId]["errors"] = $errors;
		$data[ArrowForm::FORM_CONTROL_VAR][$globalId]["proposedValues"] = $proposedValues;
		$data[ArrowForm::FORM_CONTROL_VAR][$globalId]["externalAlerts"] = $alerts;
		ArrowView::setPageInput( $data );
	}
	
}
?>